﻿//
// Copyright (c) 2024 BPer
//

#include <bperice/BPerRtcStun.h>
#include <bperice/BPerRtcSocket.h>

#include <bperrtc/BPerRtcRtcp.h>
#include <bperrtc/BPerRtcConnection.h>
#include <bperrtp/BPerRtpConstant.h>

#include <bperutil/sys/BPerLog.h>

#include <bperutil/sys/BPerSocket.h>
#include <bperutil/sys/BPerEndian.h>
#include <bperutil/BPerAvInfoType.h>
#include <bperutil/sys/BPerCString.h>
#include <bperutil/buffer/BPerBuffer.h>

#include <bperssl/BPerSsl.h>


uint32_t bPer_stun_encode_hmac2(char* hmac_buf, const int32_t hmac_buf_len,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,StunMessageIntegrity);
	bPer_write_2bytes(&stream,hmac_buf_len);
	bPer_write_bytes(&stream,hmac_buf, hmac_buf_len);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_fingerprint2(uint32_t crc32,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,(int16_t)StunFingerprint);
	bPer_write_2bytes(&stream,4);
	bPer_write_4bytes(&stream,crc32);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_hmac(BPerBuffer *pstream,char* pwd,char** str)
{
	uint32_t ret=0;
	char buf[1460];
	char hmac_buf[20] = {0};
	uint32_t  hmac_buf_len = 0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	if ((ret=bPer_hmac_encode("sha1", pwd, bPer_strlen(pwd), pstream->data, bPer_buffer_pos(pstream), hmac_buf, &hmac_buf_len)) != BPer_Ok) {
		return bPer_error_wrap(ret, "hmac encode failed");
	}

	bPer_write_2bytes(&stream,StunMessageIntegrity);
	bPer_write_2bytes(&stream,hmac_buf_len);
	bPer_write_bytes(&stream,hmac_buf, hmac_buf_len);

	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}

	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_fingerprint(BPerBuffer *pstream,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	//Fingerprint
	uint32_t crc32 = bPer_crc32_ieee(pstream->data, bPer_buffer_pos(pstream), 0)^ 0x5354554E;

	bPer_write_2bytes(&stream,(int16_t)StunFingerprint);
	bPer_write_2bytes(&stream,4);
	bPer_write_4bytes(&stream,crc32);


	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_lifetime(int32_t lifetime,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,(int16_t)StunLifetime);
	bPer_write_2bytes(&stream,4);
	bPer_write_4bytes(&stream,lifetime);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_channelNumber(uint16_t channelNum,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));
	uint16_t reserve=0;
	bPer_write_2bytes(&stream,(int16_t)StunChannelNumber);
	bPer_write_2bytes(&stream,4);
	bPer_write_2bytes(&stream,channelNum);
	bPer_write_2bytes(&stream,reserve);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;

}



uint32_t bPer_stun_encode_realm(char* realm,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,(int16_t)StunRealm);
	bPer_write_2bytes(&stream,bPer_strlen(realm));
	bPer_write_bytes(&stream,realm,bPer_strlen(realm));


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}


uint32_t bPer_stun_encode_data(char* data,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,(int16_t)StunData);
	bPer_write_2bytes(&stream,bPer_strlen(data));
	bPer_write_bytes(&stream,data,bPer_strlen(data));


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}


uint32_t bPer_stun_encode_nonce(char* nonce,uint16_t len,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,(int16_t)StunNonce);
	bPer_write_2bytes(&stream,(int16_t)len);
	bPer_write_bytes(&stream,nonce,len);


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}

uint32_t bPer_stun_encode_username(char* username,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,StunUsername);
	bPer_write_2bytes(&stream,bPer_strlen(username));
	bPer_write_bytes(&stream,username,bPer_strlen(username));


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_password(char* password,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	bPer_write_2bytes(&stream,StunPassword);
	bPer_write_2bytes(&stream,bPer_strlen(password));
	bPer_write_bytes(&stream,password,bPer_strlen(password));


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}

uint32_t bPer_stun_encode_transport(uint8_t protocol,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	char tmp[4]={0};
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));

	tmp[0]=protocol;
	bPer_write_2bytes(&stream,StunRequestTransport);
	bPer_write_2bytes(&stream,4);
	bPer_write_bytes(&stream,tmp,4);


	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}

uint32_t bPer_stun_encode_rtcusername(BPerStunPacket* pkt,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));
	char username[128];
	bPer_memset(username,0,128);
	bPer_sprintf(username, sizeof(username),"%s:%s",pkt->remote_ufrag,pkt->local_ufrag);


	bPer_write_2bytes(&stream,StunUsername);
	bPer_write_2bytes(&stream,bPer_strlen(username));
	bPer_write_bytes(&stream,username,bPer_strlen(username));


	if (bPer_buffer_pos(&stream) % 4 != 0) {
		static char padding[4] = {0};
		bPer_write_bytes(&stream,padding, 4 - (bPer_buffer_pos(&stream) % 4));
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);

	bPer_memcpy(*str,stream.data, ret);
	return ret;
}


uint32_t bPer_stun_encode_peer_address_ipv4(uint32_t address,uint16_t port,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));
	bPer_write_2bytes(&stream,StunXorPeerAddress);
	bPer_write_2bytes(&stream,8);
	bPer_write_1bytes(&stream,0); // ignore this bytes
	bPer_write_1bytes(&stream,1); // ipv4 family
	bPer_write_2bytes(&stream,port ^ (kStunMagicCookie >> 16));
	bPer_write_4bytes(&stream,address ^ kStunMagicCookie);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_mapped_address_ipv4(BPerStunPacket* pkt,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));
	bPer_write_2bytes(&stream,StunXorMappedAddress);
	bPer_write_2bytes(&stream,8);
	bPer_write_1bytes(&stream,0); // ignore this bytes
	bPer_write_1bytes(&stream,1); // ipv4 family
	bPer_write_2bytes(&stream,pkt->mapped_port ^ (kStunMagicCookie >> 16));
	bPer_write_4bytes(&stream,pkt->mapped_address ^ kStunMagicCookie);
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}

uint32_t bPer_stun_encode_mapped_address_ipv6(BPerStunPacket* pkt,struct sockaddr_in6* addr,char** str)
{
	char buf[1460];
	uint32_t ret=0;
	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, sizeof(buf));
	bPer_write_2bytes(&stream,StunXorMappedAddress);
	bPer_write_2bytes(&stream,20);
	bPer_write_1bytes(&stream,0); // ignore this bytes
	bPer_write_1bytes(&stream,2); // ipv6 family
	bPer_write_2bytes(&stream,pkt->mapped_port ^ (kStunMagicCookie >> 16));
	char cookie[4]={0x21,0x12,0xA4,0x42};
	uint8_t* p=(uint8_t*)(&addr->sin6_addr.s6_addr);

	int32_t i=0;
	for(i=0;i<4;i++){
		bPer_write_1bytes(&stream,p[i]^ cookie[i]);
	}
	for(i=0;i<12;i++){
		bPer_write_1bytes(&stream,p[i+4]^ pkt->transcation_id[i]);
	}
	ret=bPer_buffer_pos(&stream);
	*str=(char*)bPer_calloc(1,ret+1);
	bPer_memcpy(*str,stream.data, ret);
	return ret;

}


int32_t bPer_stun_encode_binding_request(BPerStunPacket* pkt, BPerBuffer* stream,char* pwd,char* username,uint32_t username_len)
{
	int32_t err = BPer_Ok;

	bPer_write_2bytes(stream,StunBindingRequest);
	bPer_write_2bytes(stream,username_len);// +mapped_address_len);
	bPer_write_4bytes(stream,kStunMagicCookie);
	bPer_write_bytes(stream,pkt->transcation_id,bPer_strlen(pkt->transcation_id));

	bPer_write_bytes(stream,username,username_len);

	char* randstr=(char*)bPer_calloc(1,32);//[16];

	//Priority
	bPer_cint32_random(30,randstr+1);
	randstr[0]=9;
	uint32_t randint=atoi(randstr);
	bPer_write_2bytes(stream,StunPriority);
	bPer_write_2bytes(stream,4);
	bPer_write_4bytes(stream,randint);
	//UseCandidate
	bPer_write_2bytes(stream,StunUseCandidate);
	bPer_write_2bytes(stream,0);

	//IceControlling
	bPer_memset(randstr,0,32);
	bPer_cstr_random(8, randstr);
	bPer_write_2bytes(stream,(int16_t)StunIceControlling);
	bPer_write_2bytes(stream,8);
	bPer_write_bytes(stream,randstr,8);
	bPer_free(randstr);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 20 + 4 ) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 20 + 4 ) & 0x000000FF);
#if BPer_Enable_Dtls
	char hmac_buf[20] = {0};
	uint32_t  hmac_buf_len = 0;
	if ((err = bPer_hmac_encode("sha1", pwd, bPer_strlen(pwd), stream->data, bPer_buffer_pos(stream), hmac_buf, &hmac_buf_len)) != BPer_Ok) {
		return bPer_error_wrap(err, "hmac encode failed");
	}

#else
	char hmac_buf[20]={0x07,0xd4,0x3d,0x32,0xa1,0xd4,0xc1,0xb1,0x9d,0xf5,0xb5,0x56,0xb5,0x56,0x6d,0x20,0x5a,0xda,0xa1,0xac};
	uint32_t  hmac_buf_len = 20;
#endif

	//MessageIntegrity
	char *hmac = NULL;
	uint32_t hmac_len = bPer_stun_encode_hmac2(hmac_buf, hmac_buf_len, &hmac);
	bPer_write_bytes(stream, hmac, hmac_len);
	bPer_free(hmac);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x000000FF);

	//Fingerprint
	uint32_t crc32 = bPer_crc32_ieee(stream->data, bPer_buffer_pos(stream), 0)^ 0x5354554E;

	char *fingerprint = NULL;
	uint32_t fingerprint_len = bPer_stun_encode_fingerprint2(crc32,&fingerprint);
	bPer_write_bytes(stream, fingerprint, fingerprint_len);
	bPer_free(fingerprint);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20) & 0x000000FF);


	return err;
}

int32_t bPer_decode_stun2( char *buf, const int32_t nb_buf) {
	int32_t err = BPer_Ok;

	BPerBuffer stream;
	bPer_init_buffer(&stream, (char*) (buf), nb_buf);

	if (bPer_buffer_left(&stream) < 20) {
		return bPer_error_wrap(ERROR_RTC_STUN,
				"ERROR_RTC_STUN invalid stun packet, size=%d", stream.size);
	}


	bPer_buffer_skip(&stream, 2);
	uint16_t message_len = bPer_read_2bytes(&stream);

	bPer_buffer_skip(&stream, 4);

	bPer_buffer_skip(&stream, 12);
	if (nb_buf != 20 + message_len) {
		return bPer_error_wrap(ERROR_RTC_STUN,
				"ERROR_RTC_STUN invalid stun packet, message_len=%d, nb_buf=%d",
				message_len, nb_buf);
	}

	while (bPer_buffer_left(&stream) >= 4) {
		//uint16_t type = bPer_read_2bytes(&stream);
		bPer_buffer_skip(&stream, 2);
		uint16_t len = bPer_read_2bytes(&stream);

		if (bPer_buffer_left(&stream) < len) {
			return bPer_error_wrap(ERROR_RTC_STUN, "invalid stun packet");
		}


		bPer_buffer_skip(&stream, len);
		// padding
		if (len % 4 != 0) {

			bPer_buffer_skip(&stream, 4 - (len % 4));
		}

	}

	return err;
}

void bPer_stun_setSessionStun(BPerRtcSession *session, char *data, int32_t nb) {
	if (session == NULL)
		return;
	if (session->context.stun.data == NULL)
		session->context.stun.data = (char*) bPer_calloc(1, nb);
	bPer_memcpy(session->context.stun.data, data, nb);
	session->context.stun.nb = nb;
}



int32_t bPer_stun_encode_binding_response(BPerStunPacket* pkt,char* pwd, BPerBuffer* stream)
{
	int32_t err = BPer_Ok;

	char* property_username=NULL;
	uint32_t property_username_len=bPer_stun_encode_rtcusername(pkt,&property_username);
	char* mapped_address=NULL;
	uint32_t mapped_address_len=bPer_stun_encode_mapped_address_ipv4(pkt,&mapped_address);


	bPer_write_2bytes(stream,StunBindingSuccessResponse);
	bPer_write_2bytes(stream,property_username_len + mapped_address_len);
	bPer_write_4bytes(stream,kStunMagicCookie);
	bPer_write_bytes(stream,pkt->transcation_id,12);
	bPer_write_bytes(stream,property_username,property_username_len);
	bPer_write_bytes(stream,mapped_address,mapped_address_len);

	bPer_free(property_username);
	bPer_free(mapped_address);
	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 20 + 4) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 20 + 4) & 0x000000FF);

#if BPer_Enable_Dtls
	char hmac_buf[20] = {0};
	uint32_t  hmac_buf_len = 0;
	if ((err = bPer_hmac_encode("sha1", pwd, bPer_strlen(pwd), stream->data, bPer_buffer_pos(stream), hmac_buf, &hmac_buf_len)) != BPer_Ok) {
		return bPer_error_wrap(err, "hmac encode failed");
	}
#else
	char hmac_buf[20]={0x07,0xd4,0x3d,0x32,0xa1,0xd4,0xc1,0xb1,0x9d,0xf5,0xb5,0x56,0xb5,0x56,0x6d,0x20,0x5a,0xda,0xa1,0xac};
	uint32_t  hmac_buf_len = 20;
#endif

	char* hmac =NULL;
	uint32_t hmac_len=bPer_stun_encode_hmac2(hmac_buf, hmac_buf_len,&hmac);
	bPer_write_bytes(stream,hmac,hmac_len);
	bPer_free(hmac);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x000000FF);

	uint32_t crc32 = bPer_crc32_ieee(stream->data, bPer_buffer_pos(stream), 0) ^ 0x5354554E;

	char* fingerprint =NULL;
	uint32_t fingerprint_len=bPer_stun_encode_fingerprint2(crc32,&fingerprint);
	bPer_write_bytes(stream,fingerprint,fingerprint_len);
	bPer_free(fingerprint);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20) & 0x000000FF);

	return err;
}

int32_t bPer_stun_createResponseStunPacket(BPerStunPacket* request,void* psession){
	BPerRtcSession* session=(BPerRtcSession*)psession;
	char s[1024] = { 0 };
	BPerBuffer stream;
	bPer_init_buffer(&stream,s, 1024);
	BPerStunPacket packet;
	bPer_memset(&packet,0,sizeof(BPerStunPacket));

	packet.mapped_address=bPer_addr_getIP(&session->context.sock->session.remote_addr);
	packet.mapped_port=bPer_addr_getPort(&session->context.sock->session.remote_addr);

	bPer_memcpy(packet.transcation_id,request->transcation_id,12);
	bPer_strcpy(packet.local_ufrag,session->local_ufrag);
	bPer_strcpy(packet.remote_ufrag,session->remote_ufrag);
	bPer_stun_encode_binding_response(&packet,session->localIcePwd,&stream);

	return session->context.sock->write(&session->context.sock->session,stream.data, bPer_buffer_pos(&stream));
}




int32_t bPer_decode_stun(BPerStunPacket* pkt,char* buf, const int32_t nb_buf)
{
	int32_t err = BPer_Ok;

	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, nb_buf);


	if (bPer_buffer_left(&stream) < 20) {
		return bPer_error_wrap(ERROR_RTC_STUN, "ERROR_RTC_STUN invalid stun packet, size=%d", stream.size);
	}

	pkt->message_type = bPer_read_2bytes(&stream);
	uint16_t message_len = bPer_read_2bytes(&stream);
	char magic_cookie[4] ;
	bPer_read_bytes(&stream,magic_cookie,4);


	bPer_read_bytes(&stream,pkt->transcation_id,12);

	if (nb_buf != 20 + message_len) {
		return bPer_error_wrap(ERROR_RTC_STUN, "ERROR_RTC_STUN invalid stun packet, message_len=%d, nb_buf=%d", message_len, nb_buf);
	}

	while (bPer_buffer_left(&stream) >= 4) {
		uint16_t type = bPer_read_2bytes(&stream);
		uint16_t len = bPer_read_2bytes(&stream);

		if (bPer_buffer_left(&stream)  < len) {
			return bPer_error_wrap(ERROR_RTC_STUN, "invalid stun packet");
		}


		char* val=(char*)bPer_calloc(len+1,1);
		bPer_read_bytes(&stream,val,len);
		// padding
		if (len % 4 != 0) {
			// bPer_read_string(&stream,4 - (len % 4));
			bPer_buffer_skip(&stream,4 - (len % 4));
			//bPer_read_bytes(&stream,4 - (len % 4));
		}

		switch (type) {
		case StunUsername: {
			bPer_memcpy(pkt->username,val,len);
			char* p = strtok(val,":");
			if(p) {
				bPer_strcpy(pkt->local_ufrag,p);
				p=strtok(NULL,":");
				if(p) {
					bPer_strcpy(pkt->remote_ufrag ,p);
				}
			}
			break;
		}

		case StunUseCandidate: {
			pkt->use_candidate = BPerTrue;
			//bPer_trace("stun use-candidate");
			break;
		}
		// @see: https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-5.1.2
		// One agent full, one lite:  The full agent MUST take the controlling
		// role, and the lite agent MUST take the controlled role.  The full
		// agent will form check lists, run the ICE state machines, and
		// generate connectivity checks.
		case StunIceControlled: {
			pkt->ice_controlled = BPerTrue;
			//bPer_trace("stun ice-controlled");
			break;
		}

		case StunIceControlling: {
			pkt->ice_controlling = BPerTrue;
			//bPer_trace("stun ice-controlling");
			break;
		}

		default: {
			break;
		}
		}
		bPer_free(val);
	}

	return err;
}


int32_t bPer_decode_rtcstun(BPerStunPacket* pkt,char* buf, const int32_t nb_buf)
{
	int32_t err = BPer_Ok;

	BPerBuffer stream ;
	bPer_init_buffer(&stream,buf, nb_buf);


	if (bPer_buffer_left(&stream) < 20) {
		return bPer_error_wrap(ERROR_RTC_STUN, "ERROR_RTC_STUN invalid stun packet, size=%d", stream.size);
	}

	pkt->message_type = bPer_read_2bytes(&stream);
	uint16_t message_len = bPer_read_2bytes(&stream);
	char magic_cookie[4] ;
	bPer_read_bytes(&stream,magic_cookie,4);

	bPer_read_bytes(&stream,pkt->transcation_id,12);

	if (nb_buf != 20 + message_len) {
		return bPer_error_wrap(ERROR_RTC_STUN, "ERROR_RTC_STUN invalid stun packet, message_len=%d, nb_buf=%d", message_len, nb_buf);
	}

	while (bPer_buffer_left(&stream) >= 4) {
		uint16_t type = bPer_read_2bytes(&stream);
		uint16_t len = bPer_read_2bytes(&stream);

		if (bPer_buffer_left(&stream)  < len) {
			return bPer_error_wrap(ERROR_RTC_STUN, "invalid stun packet");
		}


		char* val=(char*)bPer_calloc(len+1,1);
		bPer_read_bytes(&stream,val,len);
		// padding
		if (len % 4 != 0) {
			// bPer_read_string(&stream,4 - (len % 4));
			bPer_buffer_skip(&stream,4 - (len % 4));
			//bPer_read_bytes(&stream,4 - (len % 4));
		}


		switch (type) {
		case StunUsername: {
			bPer_memcpy(pkt->username,val,len);
			char* p = strtok(val,":");
			if(p) {
				bPer_strcpy(pkt->local_ufrag,p);
				p=strtok(NULL,":");
				if(p) {
					bPer_strcpy(pkt->remote_ufrag ,p);
				}
			}
			break;
		}

		case StunUseCandidate: {
			pkt->use_candidate = BPerTrue;
			//bPer_trace("stun use-candidate");
			break;
		}
		case StunXorMappedAddress:{
			//family=*(val+1)==1?IPv4:IPv6;
			pkt->mapped_port=bPer_get_be16((uint8_t*)val+2)^ ((uint16_t)(kStunMagicCookie >> 16));
			pkt->mapped_address=bPer_get_be32((uint8_t*)val+4)^ kStunMagicCookie;
			break;
		}
		// @see: https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-5.1.2
		// One agent full, one lite:  The full agent MUST take the controlling
		// role, and the lite agent MUST take the controlled role.  The full
		// agent will form check lists, run the ICE state machines, and
		// generate connectivity checks.
		case StunIceControlled: {
			pkt->ice_controlled = BPerTrue;
			//bPer_trace("stun ice-controlled");
			break;
		}

		case StunIceControlling: {
			pkt->ice_controlling = BPerTrue;
			//bPer_trace("stun ice-controlling");
			break;
		}

		default: {
			break;
		}
		}
		bPer_free(val);
	}

	return err;
}



int32_t bPer_stun_encode_binding_request2(BPerStunPacket* pkt, BPerBuffer* stream,BPerRtcSocket* udp,char* pwd,char* username,uint32_t username_len){
	int32_t err = BPer_Ok;

	//bPer_write_2bytes(stream,BindingRequest);
	bPer_write_2bytes(stream,pkt->message_type);
	bPer_write_2bytes(stream,username_len);// +mapped_address_len);
	bPer_write_4bytes(stream,kStunMagicCookie);
	bPer_write_bytes(stream,pkt->transcation_id,bPer_strlen(pkt->transcation_id));
	bPer_write_bytes(stream,username,username_len);

	char* randstr=(char*)bPer_calloc(1,32);//[16];

	//Priority
	bPer_cint32_random(30,randstr+1);
	randstr[0]=9;
	uint32_t randint=atoi(randstr);
	bPer_write_2bytes(stream,StunPriority);
	bPer_write_2bytes(stream,4);
	bPer_write_4bytes(stream,randint);

	//IceControlling
	bPer_memset(randstr,0,32);
	bPer_cstr_random(8, randstr);
	bPer_write_2bytes(stream,(int16_t)StunIceControlling);
	bPer_write_2bytes(stream,8);
	bPer_write_bytes(stream,randstr,8);
	bPer_free(randstr);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 20 + 4 ) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 20 + 4 ) & 0x000000FF);

	char hmac_buf[20] = {0};
	uint32_t  hmac_buf_len = 0;
	if ((err = bPer_hmac_encode("sha1", pwd, bPer_strlen(pwd), stream->data, bPer_buffer_pos(stream), hmac_buf, &hmac_buf_len)) != BPer_Ok) {
		return bPer_error_wrap(err, "hmac encode failed");
	}



	//MessageIntegrity
	char *hmac = NULL;
	uint32_t hmac_len = bPer_stun_encode_hmac2(hmac_buf, hmac_buf_len, &hmac);
	bPer_write_bytes(stream, hmac, hmac_len);
	bPer_free(hmac);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20 + 8) & 0x000000FF);



	char *fingerprint = NULL;
	uint32_t crc32 = bPer_crc32_ieee(stream->data, bPer_buffer_pos(stream), 0)^ 0x5354554E;
	uint32_t fingerprint_len = bPer_stun_encode_fingerprint2(crc32,&fingerprint);
	bPer_write_bytes(stream, fingerprint, fingerprint_len);
	bPer_free(fingerprint);

	stream->data[2] = ((bPer_buffer_pos(stream) - 20) & 0x0000FF00) >> 8;
	stream->data[3] = ((bPer_buffer_pos(stream) - 20) & 0x000000FF);


	return err;
}

int32_t bPer_stun_createRequestStunPacket(void* psession,char* ice_pwd){
	BPerRtcSession* session=(BPerRtcSession*)psession;
	char s[1024] = { 0 };
	BPerBuffer stream;
	bPer_init_buffer(&stream,s, 1024);
	char tid[13];
	bPer_memset(tid,0,13);
	bPer_cstr_random(12,tid);
	if(bPer_strlen(session->context.avinfo->rtc.localIp)==0){
		bPer_memset(session->context.avinfo->rtc.localIp,0,sizeof(session->context.avinfo->rtc.localIp));
		bPer_getLocalInfo(session->context.avinfo->sys.familyType,session->context.avinfo->rtc.localIp);
	}
	uint32_t addr = bPer_be32toh(bPer_inet_addr(session->context.avinfo->rtc.localIp));
	BPerStunPacket packet;
	bPer_memset(&packet,0,sizeof(BPerStunPacket));
	packet.message_type=StunBindingRequest;
	bPer_strcpy(packet.local_ufrag,session->local_ufrag);
	bPer_strcpy(packet.remote_ufrag,session->remote_ufrag);

	bPer_strcpy(packet.transcation_id,tid);
	packet.mapped_address=addr;
	packet.mapped_port=session->context.streamConfig->localPort;//session->context.udp->session.local_addr.sin_port;
	char* property_username=NULL;
	uint32_t property_username_len=bPer_stun_encode_rtcusername(&packet,&property_username);
	bPer_stun_encode_binding_request(&packet,&stream,ice_pwd,property_username,property_username_len);
	bPer_free(property_username);
	//add session stun data
	bPer_stun_setSessionStun(session,stream.data,bPer_buffer_pos(&stream));

	return BPer_Ok;
}

int32_t bPer_stun_encode(BPerStunMessageType stunType,BPerBuffer* stream,void* pudp,char* username,char* ice_pwd){
	BPerRtcSocket* udp=(BPerRtcSocket*)pudp;

	char tid[13];
	bPer_memset(tid,0,13);
	bPer_cstr_random(12,tid);
    char localIp[128]={0};
    bPer_getLocalInfo(udp->session.familyType,localIp);
    uint32_t addr = bPer_be32toh(bPer_inet_addr(localIp));


	BPerStunPacket packet;
	bPer_memset(&packet,0,sizeof(BPerStunPacket));
	packet.message_type=stunType;;


	bPer_strcpy(packet.transcation_id,tid);
	packet.mapped_address=addr;

    packet.mapped_port=bPer_addr_getSinPort(&udp->session.local_addr);

	char* property_username=NULL;
	uint32_t property_username_len=bPer_stun_encode_rtcusername(&packet,&property_username);
	bPer_stun_encode_binding_request2(&packet,stream,udp,ice_pwd,property_username,property_username_len);
	bPer_free(property_username);

	return BPer_Ok;
}


int32_t bPer_stun_encode_request(BPerBuffer* stream,void* pudp,char* username,char* ice_pwd){
	return bPer_stun_encode(StunBindingRequest,stream,pudp,username,ice_pwd);
}


int32_t bPer_turn_encode_header(BPerStunPacket* pkt, BPerBuffer* stream,int32_t len){

	bPer_write_2bytes(stream,pkt->message_type);
	bPer_write_2bytes(stream,len);//
	bPer_write_4bytes(stream,kStunMagicCookie);
	bPer_cstr_random(BPerStunTranscationIdLen,pkt->transcation_id);
	bPer_write_bytes(stream,pkt->transcation_id,bPer_strlen(pkt->transcation_id));

	return BPer_Ok;
}


void bPer_create_stun(BPerRtcStun* stun){
	stun->encodeStunServer=bPer_stun_encode_request;
	stun->decodeStunServer=bPer_decode_rtcstun;
	stun->decode=bPer_decode_stun;
	stun->createResponseStunPacket=bPer_stun_createResponseStunPacket;
	stun->createRequestStunPacket=bPer_stun_createRequestStunPacket;
	stun->decode2=bPer_decode_stun2;

}

void bPer_destroy_stun(BPerRtcStun* stun){

}

void bPer_create_stunlib(BPerStunLib* stun){
	stun->encode_header=bPer_turn_encode_header;
	stun->encode_username  = bPer_stun_encode_username;
	stun->encode_password  = bPer_stun_encode_password;
	stun->encode_transport  = bPer_stun_encode_transport;
	stun->encode_data  = bPer_stun_encode_data;
	stun->encode_realm  = bPer_stun_encode_realm;
	stun->encode_nonce  = bPer_stun_encode_nonce;
	stun->encode_lifetime  = bPer_stun_encode_lifetime;
	stun->encode_channelNumber  = bPer_stun_encode_channelNumber;
	stun->encode_peer_address_ipv4  = bPer_stun_encode_peer_address_ipv4;
	stun->encode_hmac  = bPer_stun_encode_hmac;
	stun->encode_fingerprint  = bPer_stun_encode_fingerprint;

	stun->encode_mapped_address_ipv4=bPer_stun_encode_mapped_address_ipv4;
	stun->encode_request=bPer_stun_encode_request;
}

void bPer_destroy_stunlib(BPerStunLib* stun){

}



